package org.xx.armory.spring5.mvc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.xx.armory.commons.ForLogging;

import javax.sql.DataSource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

import static org.xx.armory.commons.SysUtils.asStream;
import static org.xx.armory.spring5.mvc.ModelUtils.JSON_VIEW;

/**
 * 自检。
 * <p>检查配置项是否有效。</p>
 */
@Controller
@RequestMapping("/diagnosis")
public class SelfTestController {
    @ForLogging
    private final Logger logger = LoggerFactory.getLogger(SelfTestController.class);

    @Autowired(required = false)
    private DataSource dataSource;

    @GetMapping("/self-test")
    public ModelAndView execute(
            @RemoteAddr String clientAddress,
            Authentication token
    ) {
        final var result = new ModelAndView(JSON_VIEW);

        result.addObject("token", getToken(token));
        result.addObject("client-address", clientAddress);
        try {

            result.addObject("server-address", InetAddress.getLocalHost());
        } catch (UnknownHostException ex) {
            // ignored
        }
        result.addObject("datasource", getDataSource());
        result.addObject("environment", System.getenv());
        result.addObject("properties", System.getProperties());

        return result;
    }

    @PostMapping("/upload")
    public ModelAndView upload(
            @RequestParam("key") String key,
            @RequestParam(name = "description",
                          required = false) String description,
            @RequestParam("file") MultipartFile file
    ) {
        final var result = new ModelAndView(JSON_VIEW);

        result.addObject("key", key);
        result.addObject("description", description);
        result.addObject("fileContentType", file.getContentType());
        result.addObject("fileOriginalName", file.getOriginalFilename());
        result.addObject("fileName", file.getName());
        result.addObject("fileSize", file.getSize());

        return result;
    }

    private Map<String, Object> getToken(
            Authentication token
    ) {
        if (token == null) {
            return null;
        }

        final var result = new LinkedHashMap<String, Object>();

        result.put("authenticated", token.isAuthenticated());
        result.put("name", token.getName());

        if (token.getPrincipal() != null) {
            result.put("principal-class", token.getPrincipal().getClass());
        }
        result.put("principal", token.getPrincipal());

        result.put("authorities", asStream(token.getAuthorities()).map(String::valueOf).collect(Collectors.joining(",")));

        return result;
    }

    private Map<String, Object> getDataSource() {
        if (this.dataSource == null) {
            return null;
        }

        final var result = new LinkedHashMap<String, Object>();
        try (final var connection = dataSource.getConnection()) {
            connection.createStatement().close();

            result.put("class", dataSource.getClass().getName());
        } catch (java.sql.SQLException ex) {
            result.put("error", ex.getLocalizedMessage());
        }

        return result;
    }
}
